﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Windows.Threading;
using System.Threading;
using C4F.VistaP2P.Common;
using C4F.VistaP2P.WPF.Chat;
using C4F.VistaP2P.WPF.File;
using C4F.VistaP2P.WPF.Media;
using C4F.VistaP2P.WPF.Network;
using C4F.VistaP2P.WPF.Picture;


namespace C4F.VistaP2P.WPF.Network
{
    /// <summary>
    /// Interaction logic for NetworkMan.xaml
    /// </summary>
    public partial class NetworkManager : UserControl
    {
        
        /// <summary>
        /// the peerchannel class is put in a different thread than the UI so the UI remains responsive.
        /// </summary>
        private BackgroundWorker mPeerChannelThread;


        /// <summary>
        /// low level p2p library that communicates directly with the wcf peer2peer foundation.
        /// </summary>
        private C4F.VistaP2P.Common.P2PLib mP2PLib;



        /// <summary>
        /// Used for streaming audio through a local url
        /// </summary>
        StreamingHttpListener mAudioHttpListener;
        
        /// <summary>
        /// Used for streaming audio through a local url
        /// </summary>
        StreamingHttpListener mVideoHttpListener;
  

        /// <summary>
        /// Default autogenerated constructor that just shows the networkmanager control
        /// </summary>
        public NetworkManager()
        {
            InitializeComponent();                     
        }

        /// <summary>
        /// Updates all of the dependency properties to use the default controls if they were not set at design time.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void NetworkManager_Loaded(object sender, RoutedEventArgs e)
        {
            if (NetworkName == null)
            {
                NetworkName = tbNetwork;
            }

            if (UserName == null)
            {
                UserName = tbUserName;
            }

            if (Password == null)
            {
                Password = tbPassword;
            }

            if (SignInButton == null)
            {
                SignInButton = btnSignInButton;
            }

            if (StatusValue == null)
            {
                StatusValue = lblStatusValue;
            }
            Utility.SetLabelText(StatusValue, "Disconnected...");
            

            if (Audio != null)
            {
                Utility.SetLabelText(Audio.Title, "Audio");
            }

            if (Video != null)
            {
                Utility.SetLabelText(Video.Title, "Video");
            }

        }

        /// <summary>
        /// listens for incoming chat messages and relays it to a chat control if it is attached.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the chat message and the sender name</param>
        public void Chat_ChatChanged(object sender, ChatChangedEventArgs e)
        {
            if (Chat != null)
            {
                Chat.MessageReceived(e.Message);
            }
        }

        /// <summary>
        /// listens for incoming files and relays it to a chat control if it is attached.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the file and the sender name</param>
        public void File_FileChanged(object sender, FileChangedEventArgs e)
        {
            if (File != null)
            {
                File.SetFileReceived(e);
            }
        }

        /// <summary>
        /// listens for incoming picture messages and relays it to a chat control if it is attached.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the chat message and the sender name</param>
        public void Picture_PictureChanged(object sender, PictureChangedEventArgs e)
        {
            if (Picture != null)
            {
                Picture.SetNewPicture(e);
            }
        }
        /// <summary>
        /// listens for the Sign in button to be clicked and either signs the user into the network,
        /// or signs the user out.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void SignInButton_Click(object sender, RoutedEventArgs e)
        {
            if (mState == SystemState.LoggedOut)
            {
                Start();
            }
            else
            {
                End();
            }
        }

        /// <summary>
        /// The dependency property for the UserName textbox control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty UserNameProperty = DependencyProperty.Register(
            "UserName", typeof(TextBox), typeof(NetworkManager));


       
        /// <summary>
        /// The UserName textbox control to use for the networkmanager.
        /// </summary>
        public TextBox UserName
        {
            get
            {
                try
                {
                    return (TextBox)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(UserNameProperty); },
                       UserNameProperty);
                }
                catch
                {

                    return (TextBox)UserNameProperty.DefaultMetadata.DefaultValue;
                }
            }
            set {
                    Dispatcher.Invoke(DispatcherPriority.Background,
                      (SendOrPostCallback)delegate { SetValue(UserNameProperty, value); },
                      value);
                }
        }


        /// <summary>
        /// The dependency property for the NetworkName textbox control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty NetworkNameProperty = DependencyProperty.Register(
            "NetworkName", typeof(TextBox), typeof(NetworkManager));

        /// <summary>
        /// The NetworkName textbox control to use for the networkmanager.
        /// </summary>
        public TextBox NetworkName
        {
            get
            {
                try
                {
                    return (TextBox)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(NetworkNameProperty); },
                       NetworkNameProperty);
                }
                catch
                {

                    return (TextBox)NetworkNameProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(NetworkNameProperty, value); },
                  value);
            }
        }




        /// <summary>
        /// The dependency property for the Password textbox control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty PasswordProperty = DependencyProperty.Register(
            "Password", typeof(TextBox), typeof(NetworkManager));

        /// <summary>
        /// The Password textbox control to use for the networkmanager.
        /// </summary>
        public TextBox Password
        {
            get
            {
                try
                {
                    return (TextBox)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(PasswordProperty); },
                       PasswordProperty);
                }
                catch
                {

                    return (TextBox)PasswordProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(PasswordProperty, value); },
                  value);
            }
        }


        /// <summary>
        /// The dependency property for the SignIn button control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty SignInButtonProperty = DependencyProperty.Register(
            "SignInButton", typeof(Button), typeof(NetworkManager), new PropertyMetadata(null, new PropertyChangedCallback(OnSignInButtonChanged)));

        /// <summary>
        /// Gets or sets the SignIn Button control to use for the networkmanager.
        /// </summary>
        public Button SignInButton
        {
            get
            {
                try
                {
                    return (Button)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(SignInButtonProperty); },
                       SignInButtonProperty);
                }
                catch
                {

                    return (Button)NetworkNameProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(SignInButtonProperty, value); },
                  value);
            }               
        }

        /// <summary>
        /// update the events when the sign in button changes.
        /// </summary>
        /// <param name="d">the networkmanager</param>
        /// <param name="e">contains the previous and new button</param>
        private static void OnSignInButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            NetworkManager nm = (NetworkManager)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                Button ob = (Button)e.NewValue;               
                ob.Click -= new RoutedEventHandler(nm.SignInButton_Click);
            }
            //register for the new events.
            Button nb = (Button)e.NewValue;
            nb.Click += new RoutedEventHandler(nm.SignInButton_Click);
        }

        /// <summary>
        /// The dependency property for the StatusValue control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty StatusValueProperty = DependencyProperty.Register(
           "StatusValue", typeof(Label), typeof(NetworkManager));


        /// <summary>
        /// Gets or sets the StatusValue label control to use for the networkmanager.
        /// </summary>
        public Label StatusValue
        {
            get
            {
                try
                {
                    return (Label)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(StatusValueProperty); },
                       StatusValueProperty);
                }
                catch
                {

                    return (Label)StatusValueProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(StatusValueProperty, value); },
                  value);
            }
        }

     


        /// <summary>
        /// keeps track of the network state.
        /// </summary>
        private SystemState mState = SystemState.LoggedOut;

        /// <summary>
        /// possible network states.
        /// </summary>
        public enum SystemState
        {
            LoggedOut,
            LoggingIn,
            LoggedIn,        
        }
    
        

        #region startJoinStatus
            
        /// <summary>
        /// This is the main function that takes the input from the textboxes in the window.
        /// It launches all of the threads necessary to stream audio and also starts the peerChannel thread.  This thread
        /// starts the PeerChannelWrapper class and opens a connection to the mesh.
        /// </summary>
        protected void Start()
        {
           
            Regex regex = new Regex("[a-zA-Z0-9]");
            if (!string.IsNullOrEmpty(UserName.Text) && !string.IsNullOrEmpty(NetworkName.Text) &&
                regex.IsMatch(UserName.Text) && regex.IsMatch(NetworkName.Text)
                && !NetworkName.Text.Trim().Contains(' ') && !UserName.Text.Trim().Contains(' '))
            {
                //check initial conditions..
                if (!(null == mP2PLib && mPeerChannelThread == null))
                {
                    //error unable to start something didn't get cleaned up correctly when we left the network.
                    MessageBox.Show("An Error occurred when trying to initialize the Network. Please restart the application");
                    return;
                }

                //update the state.
                mState = SystemState.LoggingIn;
                Utility.SetButtonText(this.SignInButton, "Sign Out");
                Utility.SetLabelText(this.StatusValue, "Signing In...");

                //start up the peerchannel thread which will actually do the work
                mPeerChannelThread = new System.ComponentModel.BackgroundWorker();
                mPeerChannelThread.DoWork += new System.ComponentModel.DoWorkEventHandler(mP2PLibThread);
                mPeerChannelThread.WorkerReportsProgress = true;
                mPeerChannelThread.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(mP2PLibThreadCompleted);
                mPeerChannelThread.WorkerSupportsCancellation = true;
                mPeerChannelThread.RunWorkerAsync();

            }
            else
            {
                MessageBox.Show("Please Enter a UserName and NetworkName that contains only alpha-numeric characters");
            }
         }

        /// <summary>
        /// This is the main loop for the peerChannelThread.
        /// It creates a new instance of the PeerChannelWrapper, which opens the mesh.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="doWorkArgs">unused</param>
        private void mP2PLibThread(object sender, DoWorkEventArgs doWorkArgs)
        {
            mP2PLib = new P2PLib();
            if (false == mP2PLib.PeerChannelWrapperStart(Utility.GetTextboxText(UserName), Utility.GetTextboxText(NetworkName), Utility.GetTextboxText(Password)))
            {
                //something went wrong opening the connection...bail out 
                End();
            }            

            mP2PLib.StatusChanged += new EventHandler<StatusChangedEventArgs>(P2PLib_StatusChanged);
            mP2PLib.TextPeerChannelHelper.ChatChanged += new EventHandler<ChatChangedEventArgs>(Chat_ChatChanged);
            mP2PLib.Picture.PictureChanged += new EventHandler<PictureChangedEventArgs>(Picture_PictureChanged);
            mP2PLib.FilePeerChannelHelper.FileChanged += new EventHandler<FileChangedEventArgs>(File_FileChanged);            

            if (Audio != null)
            {
                mP2PLib.StreamedAudio.StreamChanged += new EventHandler<StreamedChangedEventArgs>(mP2P_StreamedAudioListener);
                ////initialize the audio parameters.
                mAudioHttpListener = new StreamingHttpListener(Utility.GetTextboxText(UserName) + "/audio/",
                    AudioBackgroundWorkerThreadFinished, AudioStatusMessage, mP2PLib.StreamedAudio.StartStream,
                    mP2PLib.StreamedAudio.SendStream);
            }


            if (Video != null)
            {
                mP2PLib.StreamedVideo.StreamChanged += new EventHandler<StreamedChangedEventArgs>(mP2P_StreamedVideoListener);

                mVideoHttpListener = new StreamingHttpListener(Utility.GetTextboxText(UserName) + "/video/",
                     VideoBackgroundWorkerThreadFinished, VideoStatusMessage, mP2PLib.StreamedVideo.StartStream,
                     mP2PLib.StreamedVideo.SendStream);
            }


            while (mState == SystemState.LoggingIn)
            {
                System.Threading.Thread.Sleep(100);
                progressBar1.Dispatcher.Invoke(DispatcherPriority.Normal, 
                    (ThreadStart)delegate {
                        progressBar1.Value = ((progressBar1.Value >= 100) ? 0 : progressBar1.Value + 1);
                    });
                
                if (mPeerChannelThread.CancellationPending)
                {
                    //someone told us to quit.
                    progressBar1.Dispatcher.Invoke(DispatcherPriority.Normal,
                   (ThreadStart)delegate
                    {
                        progressBar1.Value = 0;
                    });
                    break;
                }
            }
            progressBar1.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { progressBar1.Value =100; });
            //just sit and wait until the main window tells us to quit.
            //this leaves the PeerChannelWrapper instance alive and running in this thread so it can process commands.
            while (true)
            {
               
                System.Threading.Thread.Sleep(1000);
                if (mPeerChannelThread.CancellationPending)
                {
                    //someone told us to quit.
                    progressBar1.Dispatcher.Invoke(DispatcherPriority.Normal,
                   (ThreadStart)delegate
                   {
                       progressBar1.Value = 0;
                   });
                    break;
                }
            }
        }


        /// <summary>
        /// Before going back to the initial state the P2PThread the following three threads should all quit
        ///  mVideoHttpListener mAudioHttpListener  peerChannelThread
        /// As each thread completes it checks to see if the others have completed. If it is the last one
        /// then it sets the state back to initial.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        protected void mP2PLibThreadCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("An Error occurred in the P2P Thread: {0}", e.Error.Message));
            }
            else
            {
                mPeerChannelThread = null;
                if (mAudioHttpListener == null && mVideoHttpListener == null)
                {
                    Utility.SetButtonText(SignInButton, "Sign In");
                    Utility.SetLabelText(StatusValue, "Disconnected...");                    
                    mState = SystemState.LoggedOut;
                    Utility.SetControlEnabledState(UserName, true);
                    Utility.SetControlEnabledState(NetworkName, true);
                    Utility.SetControlEnabledState(Password, true);
                    Utility.SetControlEnabledState(SignInButton, true);
                }
            }
        }

        /// <summary>
        /// Logs us out of the network and shuts everything down. 
        /// </summary>
        protected void End()
        {
            if (null != mP2PLib)
            {
                mP2PLib.Close();
                mP2PLib = null;
            }
            
            if (null != mVideoHttpListener)
                mVideoHttpListener.Stop();

            if (null != mAudioHttpListener)
                mAudioHttpListener.Stop();

            if (null != mVideoHttpListener)
            {
                mVideoHttpListener.Quit();
            }

            if (null != mAudioHttpListener)
            {
                mAudioHttpListener.Quit();
            }


            if (null != mPeerChannelThread)
            {
                mPeerChannelThread.CancelAsync();
            }
        }

        /// <summary>
        /// Keep track of the state of the network. and report the results int he message box.        
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">how the status changes</param>
        protected void P2PLib_StatusChanged(object sender, C4F.VistaP2P.Common.StatusChangedEventArgs e)
        {
            if (Chat != null)
            {
                Chat.SetStatusChanged(Utility.GetTextboxText(UserName), e);
            }
            
            if (Picture != null)
            {
                Picture.SetStatusChanged(Utility.GetTextboxText(UserName), e);
            }
            
            if (File != null)
            {
                File.SetStatusChanged(Utility.GetTextboxText(UserName), e);
            }

            if (Video != null)
            {
                Video.SetStatusChanged(Utility.GetTextboxText(UserName), e);
            }

            if (Audio != null)
            {
                Audio.SetStatusChanged(Utility.GetTextboxText(UserName), e);
            }

            if (e.Member == Utility.GetTextboxText(UserName))
            {
                if (e.NewNodeJoined)
                {
                    Utility.SetLabelText(StatusValue, "Connected...");
                    mState = SystemState.LoggedIn;
                }

                if (e.NodeLeft)
                {
                    Utility.SetLabelText(StatusValue, "Disconnected...");
                    mState = SystemState.LoggedOut;
                }
            }
            else
            {
                if (e.NewNodeJoined)
                {
                    Chat_ChatChanged(this, new ChatChangedEventArgs(string.Format("{0} just joined!\n", e.Member)));
                }

                if (e.NodeLeft)
                {
                    Chat_ChatChanged(this, new ChatChangedEventArgs(string.Format("{0} just left!\n", e.Member)));
                }
            }
        }

        #endregion

        #region Picture

        /// <summary>
        /// The dependency property for the Picture control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty PictureProperty = DependencyProperty.Register(
          "Picture", typeof(PictureControl), typeof(NetworkManager), new PropertyMetadata(null, new PropertyChangedCallback(OnPictureChanged)));

        /// <summary>
        /// Gets or sets the Picture control to use for the networkmanager.
        /// </summary>
        public PictureControl Picture
        {
            get
            {
                try
                {
                    return (PictureControl)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(PictureProperty); },
                       PictureProperty);
                }
                catch
                {
                    return (PictureControl)PictureProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(PictureProperty, value); },
                  value);
            }
        }

        /// <summary>
        /// Updates network manager to listen for the picture control events.
        /// </summary>
        /// <param name="d">network manager</param>
        /// <param name="e">contains the new and old picture control</param>
        private static void OnPictureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            NetworkManager nm = (NetworkManager)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                PictureControl oc = (PictureControl)e.NewValue;
                oc.SendingPicture -= new EventHandler<PictureChangedEventArgs>(nm.Picture_SendPicture);
            }
            //register for the new events.
            PictureControl nc = (PictureControl)e.NewValue;
            nc.SendingPicture += new EventHandler<PictureChangedEventArgs>(nm.Picture_SendPicture);
        }

        /// <summary>
        /// Listens for the picture control to send a message and relays it to the low level Peer2PeerLibrary
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the picture</param>
        private void Picture_SendPicture(object sender, PictureChangedEventArgs e)
        {
            e.Packet.senderNodeName = Utility.GetTextboxText(UserName);
            mP2PLib.Picture.SendPicture(e.Packet);
        }
        #endregion


        #region Chat

        /// <summary>
        /// The dependency property for the Chat control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty ChatProperty = DependencyProperty.Register(
         "Chat", typeof(ChatControl), typeof(NetworkManager), new PropertyMetadata(null, new PropertyChangedCallback(OnChatChanged)));


        /// <summary>
        /// Gets or sets the Chat control to use for the networkmanager.
        /// </summary>
        public ChatControl Chat
        {
            get
            {
                try
                {
                    return (ChatControl)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(ChatProperty); },
                       ChatProperty);
                }
                catch
                {
                    return (ChatControl)ChatProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(ChatProperty, value); },
                  value);
            }
        }

        /// <summary>
        /// When the chat control changes this listens for the change and updates the events.
        /// </summary>
        /// <param name="d">the network manater</param>
        /// <param name="e">contains the new and old chatControl</param>
        private static void OnChatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            NetworkManager nm = (NetworkManager)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                ChatControl oldText = (ChatControl)e.NewValue;
                oldText.SendingText -= new EventHandler<ChatChangedEventArgs>(nm.text_SendTextEventHandler);
            }
            //register for the new events.
            ChatControl newText = (ChatControl)e.NewValue;
            newText.SendingText += new EventHandler<ChatChangedEventArgs>(nm.text_SendTextEventHandler);
        }

        /// <summary>
        /// listens for the chat control to send a message and relays it to the network manager.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the chat message</param>
        private void text_SendTextEventHandler(object sender, ChatChangedEventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.TextPeerChannelHelper.SendTextMessage(UserName.Text, e.Message);
            }
        }
        #endregion

        #region File
        /// <summary>
        /// The dependency property for the File control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty FileProperty = DependencyProperty.Register(
       "File", typeof(FileControl), typeof(NetworkManager), new PropertyMetadata(null, new PropertyChangedCallback(OnFileChanged)));

        /// <summary>
        /// Gets or sets the File control to use for the networkmanager.
        /// </summary>
        public FileControl File
        {
            get
            {
                try
                {
                    return (FileControl)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(FileProperty); },
                       ChatProperty);
                }
                catch
                {
                    return (FileControl)FileProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(FileProperty, value); },
                  value);
            }
        }

        /// <summary>
        /// When the File control changes this listens for the change and updates the events.
        /// </summary>
        /// <param name="d">the network manater</param>
        /// <param name="e">contains the new and old FileControl</param>
        private static void OnFileChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            NetworkManager nm = (NetworkManager)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                FileControl old = (FileControl)e.NewValue;
                old.SendingFile -= new EventHandler<FileChangedEventArgs>(nm.file_SendFileEvent);
            }
            //register for the new events.
            FileControl nf = (FileControl)e.NewValue;
            nf.SendingFile += new EventHandler<FileChangedEventArgs>(nm.file_SendFileEvent);
        }

        /// <summary>
        /// Listens to the file control and relays the message to the lower level p2p library where the file is sent out.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the file to send.</param>
        private void file_SendFileEvent(object sender, FileChangedEventArgs e)
        {
            e.Packet.senderNodeName = Utility.GetTextboxText(UserName);
            mP2PLib.FilePeerChannelHelper.SendFile(e.Packet);
        }
        #endregion


        /// <summary>
        /// The dependency property for the Audio control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty AudioControlProperty = DependencyProperty.Register(
            "Audio", typeof(MediaControl), typeof(NetworkManager), new PropertyMetadata(null, new PropertyChangedCallback(OnAudioChanged)));


        /// <summary>
        /// Gets or sets the Audio control to use for the networkmanager.
        /// </summary>
        public MediaControl Audio
        {
            get
            {
                try
                {
                    return (MediaControl)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(AudioControlProperty); },
                       AudioControlProperty);
                }
                catch
                {
                    return (MediaControl)AudioControlProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(AudioControlProperty, value); },
                  value);
            }
        }


        /// <summary>
        /// When the audio control changes this listens for the change and updates the events.
        /// </summary>
        /// <param name="d">the network manater</param>
        /// <param name="e">contains the new and old audioControl</param>
        private static void OnAudioChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            NetworkManager nm = (NetworkManager)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                MediaControl oldAudio = (MediaControl)e.NewValue;
                oldAudio.SendingStream -= new EventHandler<SendStreamEventArgs>(nm.audio_SendMediaEvent);
                oldAudio.CancelStream -= new EventHandler(nm.audio_CancelStream);
            }
            //register for the new events.
            MediaControl newAudio = (MediaControl)e.NewValue;
            
            newAudio.SendingStream += new EventHandler<SendStreamEventArgs>(nm.audio_SendMediaEvent);
            newAudio.CancelStream += new EventHandler(nm.audio_CancelStream);
            newAudio.IsMediaVideo(false); //let this one know it is an audio player
        }

        /// <summary>
        /// Listens for the audio file to stream the
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the audio file to stream</param>
        private void audio_SendMediaEvent(object sender, SendStreamEventArgs e)
        {
            if (mAudioHttpListener != null)
            {
                mAudioHttpListener.SendStreamingData(e.FullFileName);
            }
        }


        /// <summary>
        /// The dependency property for the Video control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty VideoControlProperty = DependencyProperty.Register(
            "Video", typeof(MediaControl), typeof(NetworkManager), new PropertyMetadata(null, new PropertyChangedCallback(OnVideoChanged)));



        /// <summary>
        /// Gets or sets the Video control to use for the networkmanager.
        /// </summary>
        public MediaControl Video
        {
            get
            {
                try
                {
                    return (MediaControl)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(VideoControlProperty); },
                       VideoControlProperty);
                }
                catch
                {
                    return (MediaControl)VideoControlProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Background,
                  (SendOrPostCallback)delegate { SetValue(VideoControlProperty, value); },
                  value);
            }
        }

        /// <summary>
        /// This functon listens to the Video Control and relays the streaming data to the 
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the filename to send</param>
        private void video_SendMediaEvent(object sender, SendStreamEventArgs e)
        {
            if (mVideoHttpListener != null)
            {
                mVideoHttpListener.SendStreamingData(e.FullFileName);
            }
        }


        /// <summary>
        /// When the video control changes this listens for the change and updates the events.
        /// </summary>
        /// <param name="d">the network manater</param>
        /// <param name="e">contains the new and old VideoControl</param>
        private static void OnVideoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            NetworkManager nm = (NetworkManager)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                MediaControl oldVideo = (MediaControl)e.NewValue;
                oldVideo.SendingStream -= new EventHandler<SendStreamEventArgs>(nm.video_SendMediaEvent);
                oldVideo.CancelStream -= new EventHandler(nm.video_CancelStream);
            }
            //register for the new events.
            MediaControl newVideo = (MediaControl)e.NewValue;
            newVideo.SendingStream += new EventHandler<SendStreamEventArgs>(nm.video_SendMediaEvent);
            newVideo.CancelStream += new EventHandler(nm.video_CancelStream);
            newVideo.IsMediaVideo(true); //let this one know it is a video player
        }


        /// <summary>
        /// when the audio stream changes the VideoHttpListener will raise an event that this function handles.
        /// It notifies the Video control that the status has changed.
        /// </summary>
        /// <param name="msg">the status message that changed</param>
        public void VideoStatusMessage(string msg)
        {
            if (Video != null)
            {
                Video.SetStatusMessage(msg);
            }
        }

        /// <summary>
        /// when the audio stream changes the AudioHttpListener will raise an event that this function handles.
        /// It notifies the Audio control that the status has changed.
        /// </summary>
        /// <param name="msg">the status message that changed</param>
        public void AudioStatusMessage(string msg)
        {
            if (Audio != null)
            {
                Audio.SetStatusMessage(msg);
            }
        }

        /// <summary>
        /// Relays the event containing the picture be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void audio_CancelStream(object sender, EventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.StreamedAudio.CancelSendingStream();
            }
        }

        /// <summary>
        /// Relays the event containing the picture be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void video_CancelStream(object sender, EventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.StreamedVideo.CancelSendingStream();
            }
        }

        /// <summary>
        /// This delegagte is used to make sure that the function mP2P_StreamedVideoListener is made on the audio control's
        /// thread.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the streamed data</param>
        delegate void mP2P_StreamedVideoHandler(object sender, StreamedChangedEventArgs e);

        /// <summary>
        /// This function gets called when we receive a video message. If it is the first packet in the audio stream
        /// we use a delegate and have the UI(user interface) thread call this function.  When it does CheckAccess will be true
        /// and the controls will be updated.  
        /// </summary>
        ///<param name="e">contains the streamed data</param>
        ///<param name="sender">unused</param>
        public void mP2P_StreamedVideoListener(object sender, StreamedChangedEventArgs e)
        {
            MediaControl vid = Video; //get a copy to prevent race conditions when exiting program while sending data
            if (vid != null)
            {
                
                if (vid.CheckAccess())
                {
                    bool play = false;
                    play = e.StreamedPacket.senderNodeName == UserName.Text;


                    MessageBoxResult mbr = MessageBoxResult.Cancel;
                    if (false == play)
                    {
                        mbr = MessageBox.Show(e.StreamedPacket.senderNodeName + " just sent you a video. Select OK to accept it, otherwise select Cancel", "A file was sent to you!", MessageBoxButton.OKCancel);
                    }

    
                    if (play || mbr == MessageBoxResult.OK)
                    {
                        Utility.SetLabelText(vid.LineOneMetaData, "Title:");
                        Utility.SetLabelText(vid.LineTwoMetaData, "Artist:");
                        Utility.SetLabelText(vid.LineThreeMetaData, "");

                        Utility.SetLabelText(vid.LineOneMetaDataValue, e.StreamedPacket.title);
                        Utility.SetLabelText(vid.LineTwoMetaDataValue, e.StreamedPacket.artist);

                        //Video.LineThreeMetaDataValue.Content = e.StreamedPacket.artist;
                        vid.StartPlay("http://localhost:8088/" + UserName.Text + "/video/" + StreamingHttpListener.SafeHttpString(e.StreamedPacket.fileName));
                    }
                }
                else
                {
                    mVideoHttpListener.StreamedChanged(e);
                    if (e.StreamedPacket.packetNumber == 0)
                    {
                        
                        vid.Dispatcher.Invoke(DispatcherPriority.Normal, new mP2P_StreamedVideoHandler(mP2P_StreamedVideoListener), sender, e);
                    }
                }
            }
        }


        /// <summary>
        /// This gets called when the video thread has finished. If the video thread and the peerchannel thread have all quit,
        /// then it sets the state back to initial and allows the user to join another network.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">used to look for any errors in the thread</param>
        protected void VideoBackgroundWorkerThreadFinished(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("An Error occurred in the Video Processing Thread:{0}", e.Error.Message));
            }
            else
            {
                mVideoHttpListener = null;
                if (mPeerChannelThread == null && mAudioHttpListener == null)
                {
                    Utility.SetButtonText(SignInButton, "Sign In");
                    Utility.SetLabelText(StatusValue, "Disconnected...");
                    mState = SystemState.LoggedOut;
                    Utility.SetControlEnabledState(UserName, true);
                    Utility.SetControlEnabledState(NetworkName, true);
                    Utility.SetControlEnabledState(Password, true);
                    Utility.SetControlEnabledState(SignInButton, true);
    
                }
            }
        }


        /// <summary>
        /// This delegagte is used to make sure that the function mP2P_StreamedAudioListener is made on the audio control's
        /// thread.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">contains the streamed data</param>
        delegate void mP2P_StreamedAudioHandler(object sender, StreamedChangedEventArgs e);
        
        /// <summary>
        /// This function gets called when we receive an audio message. If it is the first packet in the audio stream
        /// we use a delegate and have the UI(user interface) thread call this function.  When it does CheckAccess will be true
        /// and the controls will be updated.  
        /// </summary>
        ///<param name="e">contains the streamed data</param>
        ///<param name="sender">unused</param>
        public void mP2P_StreamedAudioListener(object sender, StreamedChangedEventArgs e)
        {
            MediaControl aud = Audio; //get a copy to prevent race conditions when exiting program while sending data            
            if (aud != null)
            {
                if (aud.CheckAccess())
                {

                    bool play = false;
                    play = e.StreamedPacket.senderNodeName == UserName.Text;

                    MessageBoxResult mbr = MessageBoxResult.Cancel;
                    if (false == play)
                    {
                        mbr = MessageBox.Show(e.StreamedPacket.senderNodeName + " just sent you audio. Select OK to accept it, otherwise select Cancel", "A file was sent to you!", MessageBoxButton.OKCancel);
                    }
                    
                    if (play || mbr == MessageBoxResult.OK)
                    {
                        Utility.SetLabelText(aud.LineOneMetaData, "Title:");
                        Utility.SetLabelText(aud.LineTwoMetaData, "Artist:");
                        Utility.SetLabelText(aud.LineThreeMetaData, "Album");

                        Utility.SetLabelText(aud.LineOneMetaDataValue, e.StreamedPacket.title);
                        Utility.SetLabelText(aud.LineTwoMetaDataValue, e.StreamedPacket.artist);
                        Utility.SetLabelText(aud.LineThreeMetaDataValue, e.StreamedPacket.album);

                        aud.StartPlay("http://localhost:8088/" + UserName.Text + "/audio/" + StreamingHttpListener.SafeHttpString(e.StreamedPacket.fileName));
                    }
                }
                else
                {
                    mAudioHttpListener.StreamedChanged(e);
                    if (e.StreamedPacket.packetNumber == 0)
                    {
                        aud.Dispatcher.Invoke(DispatcherPriority.Normal, new mP2P_StreamedAudioHandler(mP2P_StreamedAudioListener), sender, e);                        
                    }
                }
            }
        }


        /// <summary>
        /// This gets called when the audio thread has finished. If the video thread and the peerchannel thread have all quit,
        /// then it sets the state back to initial and allows the user to join another network.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">used to look for an error in the thread</param>
        protected void AudioBackgroundWorkerThreadFinished(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("An Error occurred in the Audio Processing Thread:{0}", e.Error.Message));
            }
            else
            {
                mAudioHttpListener = null;

                if (mPeerChannelThread == null && mVideoHttpListener == null)
                {
                    Utility.SetButtonText(SignInButton, "Sign In");
                    Utility.SetLabelText(StatusValue, "Disconnected...");
                    mState = SystemState.LoggedOut;
                    Utility.SetControlEnabledState(UserName, true);
                    Utility.SetControlEnabledState(NetworkName, true);
                    Utility.SetControlEnabledState(Password, true);
                    Utility.SetControlEnabledState(SignInButton, true);
                }
            }
        }
       
    }
}
